import { Parser } from './Parser.js';
import { Operator } from './Operator.js';
import { Renderer } from './Renderer.js';
import { Observer } from './Observer.js';

/** @类 逻辑类 */
export class Logic {
  constructor(el) {
    this.parser = new Parser();
    this.observer = new Observer();
    this.renderer = new Renderer({ el, observer: this.observer });
    this.operator = new Operator({
      parser: this.parser,
      observer: this.observer,
      renderer: this.renderer,
    })
    this.observer.addListener('draw', this.onDraw.bind(this));
    this.observer.addListener('zoom', this.onZoom.bind(this));
    this.observer.addListener('drag', this.onDrag.bind(this));
    this.observer.addListener('click', this.onClick.bind(this));
    this.observer.addListener('resize', this.onResize.bind(this));
  }

  /** @方法 加载项目 */
  load(data) {
    this.parser.load({
      data,
      observer: this.observer,
      renderer: this.renderer,
    });
  }

  /** @方法 销毁内容 */
  destroy() {
    this.renderer.destroy();
    this.observer.destroy();
    this.operator.destroy();
  }

  /** @事件 尺寸变化 */
  onResize() {
    const { parser, renderer } = this;
    const { background, scale } = parser;
    scale.rate = renderer.width / background.width; // 更新缩放率
  }

  /** @事件 渲染画面 */
  onDraw() {
    const { parser, renderer } = this;
    const { canvas, buffer, bufferCtx, canvasCtx } = renderer;
    const { background, scale, wrapper, activeStage } = parser;
    const scaling = scale.scaling * scale.rate;
    const $PX = (v) => background.offsetX + (v * scaling);
    const $PY = (v) => background.offsetY + (v * scaling);
    const $PS = (v) => v * scaling;
    // 背景必须存在
    if (background.width <= 0 || background.height <= 0) { return; }
    // 绘制背景
    bufferCtx.restore();
    background.image && bufferCtx.drawImage(
      background.image,
      background.offsetX,
      background.offsetY,
      $PS(background.width),
      $PS(background.height)
    );
    // 绘制地块
    bufferCtx.lineWidth = wrapper.lineWidth;
    bufferCtx.strokeStyle = wrapper.lineColor;
    parser.stages.forEach(({ polygon }) => {
      bufferCtx.beginPath();
      polygon.forEach(({ x, y }, i) => {
        i === 0 ? bufferCtx.moveTo($PX(x), $PY(y)) : bufferCtx.lineTo($PX(x), $PY(y));
      });
      bufferCtx.closePath();
      bufferCtx.stroke();
    });
    // 绘制阴影 to 缓存区
    if (activeStage) {
      bufferCtx.save();
      // 绘制遮罩
      bufferCtx.fillStyle = wrapper.backgroundColor;
      bufferCtx.beginPath();
      bufferCtx.rect(0, 0, $PS(background.width), $PS(background.height));
      bufferCtx.closePath();
      bufferCtx.fill();
      // 绘制挖空区域
      bufferCtx.beginPath();
      activeStage.polygon.forEach(({ x, y }, i) => {
        i === 0 ? bufferCtx.moveTo($PX(x), $PY(y)) : bufferCtx.lineTo($PX(x), $PY(y));
      });
      bufferCtx.closePath();
      bufferCtx.clip();
      // 将背景绘制到挖空区域
      background.image && bufferCtx.drawImage(
        background.image,
        background.offsetX,
        background.offsetY,
        $PS(background.width),
        $PS(background.height)
      );
    }
    // 绘制缓冲区 to 画板
    canvasCtx.drawImage(buffer, 0, 0, canvas.width, canvas.height);
  }

  /** @事件 触发拖动 */
  onDrag({ point, downPoint, downOffset }) {
    const { scale, background, activeStage } = this.parser;
    // 如果有选中的地块，则不允许拖动
    if (activeStage) { return; }

    // 计算偏移位置
    const newOffset = this.GetLimitOffset({
      x: downOffset.x - (downPoint.x - point.x),
      y: downOffset.y - (downPoint.y - point.y),
      scaling: scale.scaling * scale.rate,
    });

    // 更新偏移位置
    background.offsetX = newOffset.x;
    background.offsetY = newOffset.y;
  }

  /** @事件 放大缩小（居中缩放） */
  onZoom({ zoom }) {
    const { parser } = this;
    const { scale, background, activeStage } = parser;
    // 如果有选中的地块，则不允许缩放
    if (activeStage) { return; }
    // 计算缩放比例
    let newScaling = zoom ? scale.scaling + scale.step : scale.scaling - scale.step;
    // 限制缩放比例
    newScaling = Math.max(scale.min, Math.min(newScaling, scale.max));
    // 计算前后缩放比例
    const scalingBefore = scale.scaling * scale.rate;
    const scalingAfter = newScaling * scale.rate;
    /** ！！！注意！关于居中缩放算法还是有问题的，现在是居于背景中间，而不是当前画布renderer.canvas中心！！！ */
    // 计算画布中心点在缩放前后的位置差
    const diffX = (background.width * scalingAfter / 2) - (background.width * scalingBefore / 2);
    const diffY = (background.height * scalingAfter / 2) - (background.height * scalingBefore / 2);
    // 计算偏移位置
    const newOffset = this.GetLimitOffset({
      x: background.offsetX - diffX,
      y: background.offsetY - diffY,
      scaling: scalingAfter,
    });
    // 更新缩放比例
    scale.scaling = newScaling;
    // 更新偏移位置
    background.offsetX = newOffset.x;
    background.offsetY = newOffset.y;
  }

  /** @事件 点击触发 */
  onClick({ point }) {
    const { parser, renderer } = this;
    const { canvas } = renderer;
    const { scale, background } = parser;
    const scaling = scale.scaling * scale.rate; // 其中 scale.rate = 画布宽度 / 背景宽度
    if (parser.activeStage) {
      // 如果已经有选中的地块，则取消选中
      parser.unStage();
      this.observer.dispatch('stage', { stage: null });
    } else if (parser.clickStage({
      x: Math.abs(background.offsetX - point.x) / scaling,
      y: Math.abs(background.offsetY - point.y) / scaling,
    })) {
      // 如果没有选中的地块，则判断是否点击到地块
      this.observer.dispatch('stage', {
        stage: parser.activeStage,
        isTop: point.y < canvas.height / 2,
        isLeft: point.x < canvas.width / 2,
      });
    }
  }

  /** @辅助 限制不能超出画布 */
  GetLimitOffset({ x, y, scaling }) {
    const { canvas } = this.renderer;
    const { background } = this.parser;
    const max = {
      x: 0 - (background.width * scaling - canvas.width),
      y: 0 - (background.height * scaling - canvas.height),
    };
    return {
      x: Math.min(Math.max(x, max.x), 0),
      y: Math.min(Math.max(y, max.y), 0),
    };
  }
}